本來想說剩下的幾天要再加入透過 Line 進行第三方登入的功能,畢竟除了 Gmail,身活在台灣 Line 也是必備的通訊軟體,能有相關的實作經驗感覺是很不錯的經歷。
但想了一想,還是決定先來嘗試看看沒試過的功能,盡量在30天內以完整這個AuthService的微服務為主要目標。
考量現代大多數網站很多都以信箱作為帳號使用,為了提升安全性、避免濫用以及確認溝通管道暢通,驗證信箱變成身分驗證系統必備的功能(好比前幾天我們從 Google 收到的 id_token 中也包含了 Email 驗證與否的 flag)。因此,今天決定學習如何實作 Email 驗證功能。
Email 驗證會在使用者填寫完註冊資訊後發生,因此原本的註冊流程會有些微變動,整體流程如下:
POST users/register
) 發起 API 請求。AuthService
接收到請求後,在同一個交易 中執行以下操作:
users
表中,建立一筆新的使用者紀錄,並將其 isEnabled
欄位設為 false
。VerificationToken
,將其與剛剛建立的使用者關聯,並存入 verification_tokens
表中。EmailService
非同步請求郵件伺服器(例如 Gmail 的 SMTP 服務),寄送一封包含驗證連結(連結中含有 VerificationToken
)的 Email。201 Created
),告知前端註冊流程已啟動。VerificationToken
的值。POST users/verify-email
) 發起 API 請求,並在請求體中附上剛剛解析出來的 token
。AuthService
接收到請求後,拿著 token
去 verification_tokens
資料表中進行查找。users
表中對應使用者的 isEnabled
欄位更新為 true
。verification_tokens
表中刪除這個已經被使用過的 Token。200 OK
)。因為這次的設計也仰賴前端元件傳遞驗證資訊,並會建立一張新的資料表來儲存驗證 Token 的資訊,讓我們對照上面的流程,大概列出完成這次功能需要新增、調整的內容:
VerificationToken
Entity,用來儲存一次性的驗證權杖。VerificationToken
應包含 token
字串、與 UserEntity
的關聯 (外鍵),以及一個 expiryDate
(過期時間)。AuthServiceImpl.registerUser
):
UserEntity
的 isEnabled
屬性預設為 false
。VerificationToken
並存入資料庫。EmailService
,觸發寄送驗證郵件的動作。201 Created
或 200 OK
並附帶提示訊息)。users/verify-email
端點,用來接收前端傳來的 token
並執行驗證。token
參數,並向後端的 users/verify-email
端點發起請求。我們將使用 spring-boot-starter-mail
來完成郵件寄送。這個套件會根據我們的設定檔,自動配置一個 JavaMailSender
的 Bean。這讓我們可以直接注入並使用它來寄送 Email,而無需處理任何底層的實作細節,實現了開箱即用的效果。一樣我們需要先將他加入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
我們透過 Gmail 來發出驗證信,不會使用自己的帳號密碼進行驗證,而是會另外申請一組應用程式密碼,就像 secret 一樣需要保密,但若外洩的情況也可以進行撤銷,不會影響原本該 Google 用戶的登入密碼。
直接搜尋 「Google 應用程式密碼」,或由此設定應用程式密碼。僅需輸入這組密碼的名稱即可完成設定,完成後會跳出應用程式密碼共16碼,記下該組密碼讓我們在後續配置環境變數時帶入。
我們需要配置 Email 服務商的 SMTP 資訊、寄件者資訊,以及驗證與否、是否加密傳輸等設定:
# SMTP 伺服器主機
spring.mail.host=smtp.gmail.com
# SMTP 伺服器port (使用 TLS)
spring.mail.port=587
# Gmail 帳號
spring.mail.username=<你的信箱>
# 應用程式密碼
spring.mail.password=<剛剛申請的應用程式密碼>
# 連上 SMTP 伺服器後,指定透過上述提供的資訊執行身份驗證,避免寄出的信被歸類為垃圾信件
spring.mail.properties.mail.smtp.auth=true
# 以加密模式傳送郵件內容,通常設定為true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
同前面設定環境變數的過程,我一樣是透過 .env 的方式傳入應用程式密碼。
今天的內容較單純,我們把註冊帳號時需要驗證 Email 的功能流程進行了梳理,清楚了從使用者註冊到帳號順利開通的流程,也只整理了目前前、後端架構或程式碼上需要做什麼調整,亦完成了前置作業與環境的配置。明天,我們就著手調整程式碼,並且進行功能測試吧:)!